﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;

namespace UtilZ.Dotnet.Ex.Communication.Net.Web
{
    /// <summary>
    /// 消息基类
    /// </summary>
    public abstract class WebSocketMessageAbs : IWebSocketMessage
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="messageType">消息类型</param>
        public WebSocketMessageAbs(WebSocketMessageTypes messageType)
        {
            this._messageType = messageType;
        }


        private readonly WebSocketMessageTypes _messageType;
        /// <summary>
        /// 获取消息类型
        /// </summary>
        public WebSocketMessageTypes MessageType
        {
            get { return _messageType; }
        }




        /// <summary>
        /// 发送消息到客户端
        /// </summary>
        /// <param name="stream"></param>
        public void Send(Stream stream)
        {
            /*************************************************************************************
             * 发送数据有两种场景
             * 场景一:如果一帧就发送,则FIN为1,Opcode为消息相应的值
             * 
             * 场景二:如果消息分片为多帧发送,则按以下规则发送.发送过程中可以插入控制类消息
             * 设文本消息分为4帧发送,文本消息的Opcode为1
             * 帧索引  FIN     Opcode      des
             * 1        0       1           第一帧FIN0,Opcode为消息类型
             * 2        0       0           第二帧到倒数第二帧FIN和Opcode都为0
             * 3        0       0
             * 4        1       0           最后一帧表示是消息关闭帧,FIN为1,Opcode为0
             *************************************************************************************/


            CPUTypes cpuType = CPUTypes.LittleEndian;
            byte opcode = this.PrimitiveGetOpcode();
            byte[] messageOpcodeBytes = ConvertEx.ToBitValues(opcode, CPUTypes.LittleEndian);
            string messageOpcodeByteStr = string.Join(string.Empty, messageOpcodeBytes.Skip(4));
            string durationFrameOpcodeByteStr = null;

            StringBuilder sb = new StringBuilder();
            FrameInfo frameInfo = new FrameInfo();
            frameInfo.FrameMaxSize = 1024;
            byte[] frameData, payloadLengthBytes;
            byte[] frameBuffer;
            byte h1, h2;
            int dataLen, payloadLen;

            using (var ms = new MemoryStream())
            {
                var bw = new BinaryWriter(ms);
                while (true)
                {
                    ms.Position = 0;
                    this.GetFrameData(frameInfo);

                    //第一个字节
                    if (frameInfo.FrameCount <= 1)
                    {
                        sb.Append("1000");//fin-rsv1-rsv2-rsv3
                        sb.Append(messageOpcodeByteStr);//opcode                
                    }
                    else
                    {
                        if (frameInfo.FrameIndex == 1)
                        {
                            sb.Append("0000");//fin-rsv1-rsv2-rsv3
                            sb.Append(messageOpcodeByteStr);//opcode             
                        }
                        else if (frameInfo.FrameIndex < frameInfo.FrameCount)
                        {
                            sb.Append("0000");//fin-rsv1-rsv2-rsv3
                            if (durationFrameOpcodeByteStr == null)
                            {
                                durationFrameOpcodeByteStr = this.GetDurationFrameOpcodeByteStr();
                            }

                            sb.Append(durationFrameOpcodeByteStr);//opcode           
                        }
                        else
                        {
                            sb.Append("1000");//fin-rsv1-rsv2-rsv3
                            sb.Append(durationFrameOpcodeByteStr);//opcode     
                        }
                    }
                    h1 = ConvertEx.BitStrToByte(sb.ToString(), cpuType);
                    bw.Write(h1);
                    sb.Clear();


                    frameData = frameInfo.FrameData;
                    //第二字节
                    sb.Append("0");//mask 服务端往客户端发送时不需要掩码
                    dataLen = frameData == null ? 0 : frameData.Length;
                    //Payloadlen
                    if (dataLen > 125)
                    {
                        if (dataLen > UInt16.MaxValue)
                        {
                            payloadLen = 127;
                        }
                        else
                        {
                            payloadLen = 126;
                        }
                    }
                    else
                    {
                        payloadLen = dataLen;
                    }

                    payloadLengthBytes = ConvertEx.ToBitValues((byte)payloadLen, CPUTypes.LittleEndian);
                    //Payload len(7)
                    for (int i = 1; i < payloadLengthBytes.Length; i++)
                    {
                        sb.Append(payloadLengthBytes[i]);
                    }
                    h2 = ConvertEx.BitStrToByte(sb.ToString(), cpuType);
                    bw.Write(h2);
                    sb.Clear();

                    //数据长度较大,扩展载荷数据长度
                    payloadLengthBytes = null;
                    if (payloadLen == 126)
                    {
                        //2byte
                        payloadLengthBytes = BitConverter.GetBytes((UInt16)dataLen);
                    }
                    else if (payloadLen == 127)
                    {
                        //8byte
                        payloadLengthBytes = BitConverter.GetBytes((UInt64)dataLen);
                    }

                    if (payloadLengthBytes != null)
                    {
                        for (int i = payloadLengthBytes.Length - 1; i >= 0; i--)
                        {
                            bw.Write(payloadLengthBytes[i]);
                        }
                    }

                    if (dataLen > 0)
                    {
                        bw.Write(frameData);
                    }
                    bw.Flush();

                    frameBuffer = ms.GetBuffer().Take((int)ms.Position).ToArray();
                    stream.Write(frameBuffer, 0, frameBuffer.Length);

                    if (frameInfo.FrameIndex >= frameInfo.FrameCount)
                    {
                        break;
                    }
                    else
                    {
                        this.SliceContinueSend();//判断是否继续发送
                    }
                }
            }
        }


        /// <summary>
        /// 获取操作码
        /// </summary>
        /// <returns>操作码</returns>
        protected abstract byte PrimitiveGetOpcode();

        /// <summary>
        /// 获取本次发送帧的数据(返回true表示还有数据需要发送,返回false表示最后一帧数据)
        /// </summary>
        /// <param name="frameInfo">帧数据</param>
        /// <returns>true表示还有数据需要发送,返回false表示最后一帧数据</returns>
        protected abstract void GetFrameData(FrameInfo frameInfo);

        private string _durationFrameOpcodeByteStr = null;
        /// <summary>
        /// 获取持续帧操作码二进制字符串
        /// </summary>
        /// <returns>持续帧操作码二进制字符串</returns>
        protected string GetDurationFrameOpcodeByteStr()
        {
            if (this._durationFrameOpcodeByteStr == null)
            {
                byte[] opcodeBytes = ConvertEx.ToBitValues(HttpOpcodes.DURATION_FRAME, CPUTypes.LittleEndian);
                this._durationFrameOpcodeByteStr = string.Join(string.Empty, opcodeBytes.Skip(4));
            }

            return this._durationFrameOpcodeByteStr;
        }



        /// <summary>
        /// 获取操作码
        /// </summary>
        /// <returns>操作码</returns>
        public byte GetOpcode()
        {
            return this.PrimitiveGetOpcode();
        }




        private bool _paused = false;
        private AutoResetEvent _sliceHandler = null;
        private AutoResetEvent _controlHandler = null;
        private void SliceContinueSend()
        {
            if (this._paused)
            {
                this._controlHandler.Set();
                while (this._paused)
                {
                    if (this._sliceHandler.WaitOne(500))
                    {
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// 暂停发送
        /// </summary>
        public void Pause()
        {
            lock (this)
            {
                if (this._sliceHandler == null)
                {
                    this._sliceHandler = new AutoResetEvent(false);
                    this._controlHandler = new AutoResetEvent(false);
                }
            }

            this._paused = true;
            this._controlHandler.WaitOne();
        }

        /// <summary>
        /// 继续发送
        /// </summary>
        public void Resume()
        {
            this._paused = false;

            var handler = this._sliceHandler;
            if (handler != null)
            {
                handler.Set();
            }
        }

        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);

            lock (this)
            {
                if (this._sliceHandler != null)
                {
                    this._sliceHandler.Dispose();
                    this._controlHandler.Dispose();
                }
            }
        }

        /// <summary>
        /// Dispose
        /// </summary>
        /// <param name="disposeing">bool</param>
        protected virtual void Dispose(bool disposeing)
        {

        }
    }
}
